#include "defaultwindowfinder.h"


namespace UDock {

DefaultWindowFinder DefaultWindowFinder::m_wf;

Window realWindowFromPoint( Display* display, const Point &point, const std::unordered_set<Window>& widgets_to_ignore);


Window DefaultWindowFinder::windowFromPoint(const Point &point, const std::unordered_set<Window>& widgets_to_ignore)
{
    std::unordered_set<Window> widgets_to_ignore_cleaned;
    Display* display = XOpenDisplay(0);
    for (Window win:widgets_to_ignore){
        Window parent = 0;
        Window root_return = 0;
        Window* children = 0;
        unsigned int children_count = 0;
        Status stat = XQueryTree(display,win,&root_return
                                 ,&parent,&children,&children_count);
        XFree(children);
        if (stat){
            while (parent != 0 && parent != root_return){
                win = parent;
                stat = XQueryTree(display,win,&root_return
                                                 ,&parent,&children,&children_count);
                XFree(children);
            }
            widgets_to_ignore_cleaned.insert(win);
        }

    }
    auto ret =  realWindowFromPoint(display, point, widgets_to_ignore_cleaned);
    XCloseDisplay(display);
    return ret;
}


//#ifdef linux// linux specific implementations
//TODO: support other configurations than X11

#include <X11/Xlib.h>


//Window getParentWId(Window wid) {
////    Display* display = QX11Info::display();
//    Display* display = XOpenDisplay(0);
////    Q_ASSERT_X(display,"toplevelWindowList", "Display is null.");

//    Window parent = 0;
//    Window root_return = 0;
//    Window* children = 0;
//    unsigned int children_count = 0;
//    Status stat = XQueryTree(display,wid,&root_return
//                             ,&parent,&children,&children_count);
//    XFree(children);
////    Q_ASSERT_X(stat, "toplevelWindowList", "XQueryTree failed.");
//    return Window(parent);
//}

//std::vector<Window> toplevelWindowList(){

//    // IDEA:
//    // go up until you find the desktop widget
//    // compare the number of steps until you reach the
//    // id of the widget
//    // set a bool to reparent or not.

//    std::vector<Window> result;

//    Display* display = XOpenDisplay(0);
////    Q_ASSERT_X(display,"toplevelWindowList", "Display is null.");
//    Window root = XDefaultRootWindow(display);
////    Q_ASSERT_X(root, "toplevelWindowList", "Could not query root window");

//    Window parent = 0;
//    Window root_return = 0;
//    Window* children = 0;
//    unsigned int children_count = 0;
//    Status stat = XQueryTree(display,root,&root_return
//                             ,&parent,&children,&children_count);
////    Q_ASSERT_X(stat, "toplevelWindowList", "XQueryTree failed.");

//    if (!children)
//        return result;

//    int i;
//    // reverse the list that we obtained from XQueryTree
//    for (i = children_count-1;i >= 0 ; --i) {
//        XWindowAttributes a;
//        Window w = children[i];
//        stat = XGetWindowAttributes(display, w, &a);
//        if (!stat) continue;

//        // A window that has an InputOnly property cannot be drawn to. this
//        // means, that it is not of interest, as it cannot be visible.
//        if (a.map_state == IsViewable && a.c_class == InputOutput)
//            result.push_back(w );
//    }
//    XFree(children);

//    return result;
//}

Window realWindowFromPoint(Display* display, const Point &point, const std::unordered_set<Window>& widgets_to_ignore)
{
//    Q_ASSERT_X(display,"toplevelWindowList", "Display is null.");
    Window root = XDefaultRootWindow(display);
//    Q_ASSERT_X(root, "toplevelWindowList", "Could not query root window");

    Window parent = 0;
    Window root_return = 0;
    Window* children = 0;
    unsigned int children_count = 0;
    Status stat = XQueryTree(display,root,&root_return
                             ,&parent,&children,&children_count);
//    Q_ASSERT_X(stat, "toplevelWindowList", "XQueryTree failed.");

    if (!children)
        return 0;

    int i;
    auto end = widgets_to_ignore.end();
    // reverse the list that we obtained from XQueryTree
    for (i = children_count-1;i >= 0 ; --i) {
        XWindowAttributes a;
        Window w = children[i];
        if (widgets_to_ignore.find(w) != end)
            continue;
        stat = XGetWindowAttributes(display, w, &a);
        if (!stat) continue;

        // A window that has an InputOnly property cannot be drawn to. this
        // means, that it is not of interest, as it cannot be visible.
        if (a.map_state == IsViewable && a.c_class == InputOutput){

            if (a.x <= point.x && a.y <= point.y && (a.width + a.x) > point.x && (a.height + a.y) > point.y){
                XFree(children);
                return w;
            }
        }

    }
    XFree(children);
    return 0;
}


Rect getWidRect(Display* display, Window wid){
    XWindowAttributes a;
//    Display* display = QX11Info::display();
//    Q_ASSERT_X(display,"getWidRect", "Could not query the dislay" \
//               "using XQueryDisplay");
    Status ret = XGetWindowAttributes(display, (Window)wid, &a);
//    Q_ASSERT_X(ret,"getWidRect", "Could not query window attributes" \
//               "using XGetWindowAttributes");
    return Rect(a.x, a.y, a.width, a.height);
}

bool DefaultWindowFinder::isFirstDescendantOfSecond(Window first, Window second)
{
    Display* display = XOpenDisplay(0);
    Window ancestor= first;
    Window root_return = 0;
    while (ancestor != second && ancestor != root_return){
    //    Q_ASSERT_X(display,"toplevelWindowList", "Display is null.");

        Window parent = 0;
        Window* children = 0;
        unsigned int children_count = 0;
        Status stat = XQueryTree(display,ancestor,&root_return
                                 ,&parent,&children,&children_count);
        XFree(children);
        ancestor = parent;

    }
    XCloseDisplay(display);
    if (ancestor == second)
        return true;
    return false;
}

DefaultWindowFinder* DefaultWindowFinder::getDefaultWindowFinder() {
    return &m_wf;
}

}
//#endif
